﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Net;
using System.IO;

namespace DevoPoint.Framework.Extensions.BingTranslator
{
    [StringValue("BingLanguages")]
    public enum BingLanguage
    {
        [StringValue("None")]
        None,
        [StringValue("ar")]
        Arabic,
        [StringValue("bg")]
        Bulgarian,
        [StringValue("ca")]
        Catalan,
        [StringValue("zh-CHS")]
        ChineseSimplified,
        [StringValue("zh-CHT")]
        ChineseTraditional,
        [StringValue("cs")]
        Czech,
        [StringValue("da")]
        Danish,
        [StringValue("nl")]
        Dutch,
        [StringValue("en")]
        English,
        [StringValue("et")]
        Estonian,
        [StringValue("fi")]
        Finnish,
        [StringValue("fr")]
        French,
        [StringValue("de")]
        German,
        [StringValue("el")]
        Greek,
        [StringValue("ht")]
        HaitianCreole,
        [StringValue("he")]
        Hebrew,
        [StringValue("hu")]
        Hungarian,
        [StringValue("id")]
        Indonesian,
        [StringValue("it")]
        Italian,
        [StringValue("ja")]
        Japanese,
        [StringValue("ko")]
        Korean,
        [StringValue("lv")]
        Latvian,
        [StringValue("lt")]
        Lithuanian,
        [StringValue("no")]
        Norwegian,
        [StringValue("pl")]
        Polish,
        [StringValue("pt")]
        Portuguese,
        [StringValue("ro")]
        Romanian,
        [StringValue("ru")]
        Russian,
        [StringValue("sk")]
        Slovak,
        [StringValue("sl")]
        Slovenian,
        [StringValue("es")]
        Spanish,
        [StringValue("sv")]
        Swedish,
        [StringValue("th")]
        Thai,
        [StringValue("tr")]
        Turkish,
        [StringValue("uk")]
        Ukrainian,
        [StringValue("vi")]
        Vietnamese
    }

    public static class BingTranslationExtension
    {
        /// <summary>
        /// The Bing Application Developer ID, can be generated using Windows Live Account here: http://www.bing.com/developers/appids.aspx
        /// </summary>
        public static string BingAppID { get; set; }
        
        private static Dictionary<string, BingLanguage> langsDic = new Dictionary<string, BingLanguage>() {
        { "None",BingLanguage.None},
        {"ar",BingLanguage.Arabic},
        {"bg",BingLanguage.Bulgarian},
        {"ca",BingLanguage.Catalan},
        {"zh-CHS",BingLanguage.ChineseSimplified},
        {"zh-CHT",BingLanguage.ChineseTraditional},
        {"cs",BingLanguage.Czech},
        {"da",BingLanguage.Danish},
        {"nl",BingLanguage.Dutch},
        {"en",BingLanguage.English},
        {"et",BingLanguage.Estonian},
        {"fi",BingLanguage.Finnish},
        {"fr",BingLanguage.French},
        {"de",BingLanguage.German},
        {"el",BingLanguage.Greek},
        {"ht",BingLanguage.HaitianCreole},
        {"he",BingLanguage.Hebrew},
        {"hu",BingLanguage.Hungarian},
        {"id",BingLanguage.Indonesian},
        {"it",BingLanguage.Italian},
        {"ja",BingLanguage.Japanese},
        {"ko",BingLanguage.Korean},
        {"lv",BingLanguage.Latvian},
        {"lt",BingLanguage.Lithuanian},
        {"no",BingLanguage.Norwegian},
        {"pl",BingLanguage.Polish},
        {"pt",BingLanguage.Portuguese},
        {"ro",BingLanguage.Romanian},
        {"ru",BingLanguage.Russian},
        {"sk",BingLanguage.Slovak},
        {"sl",BingLanguage.Slovenian},
        {"es",BingLanguage.Spanish},
        {"sv",BingLanguage.Swedish},
        {"th",BingLanguage.Thai},
        {"tr",BingLanguage.Turkish},
        {"uk",BingLanguage.Ukrainian},
        {"vi",BingLanguage.Vietnamese}
        };

        /// <summary>
        /// Detect the language of the text and identify the language if the selected text.
        /// </summary>
        /// <returns>Returns the language of the text</returns>
        public static BingLanguage DetectLanguage(this string text)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (text.Trim().Length == 0) return BingLanguage.None;
            try
            {
                BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
                string DetectedLangLocale = client.Detect(BingAppID, text);
                return langsDic[DetectedLangLocale];
            }
            catch (Exception)
            {
                
                throw;
            }
            
        }

        /// <summary>
        /// Translates the texts from source language to destination language
        /// </summary>
        /// <param name="source">Source Language</param>
        /// <param name="destination">Destination Language</param>
        /// <returns>Returns the translated text</returns>
        public static string BingTranslate(this string text, BingLanguage source, BingLanguage destination)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (text.Trim().Length == 0) return "";
            BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
            return client.Translate(BingAppID, text, source.GetBingLangCode(), destination.GetBingLangCode(), "text/plain", "general");
        }

        /// <summary>
        /// Auto detects the language of the text and translate it into the destination language
        /// </summary>
        /// <param name="destination">Destination Language</param>
        /// <returns>Returns the translated text</returns>
        public static string BingTranslate(this string text, BingLanguage destination)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (text.Trim().Length == 0) return "";
            BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
            
            return client.Translate(BingAppID, text, text.DetectLanguage().GetBingLangCode(), destination.GetBingLangCode(), "text/plain", "general");
        }
        
        /// <summary>
        /// Get the translation of the language name in a specified language
        /// </summary>
        /// <param name="destinationlang">Destination Language</param>
        /// <returns>Returns the name of the language translated </returns>
        public static string GetLanguageNameTranslation(this BingLanguage binglang, BingLanguage destinationlang)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (binglang == BingLanguage.None || destinationlang == BingLanguage.None)
            {
                throw new Exception("Invalid Language");
            }

            BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
            string[] destlangs = new string[] { binglang.GetBingLangCode()};
            return client.GetLanguageNames(BingAppID, destinationlang.GetBingLangCode(), destlangs).First();
        }

        /// <summary>
        /// Retrieve the language cutlture ID of specific language
        /// </summary>
        /// <returns>Returns the culture ID of specific language</returns>
        public static string GetBingLangCode(this BingLanguage value)
        {
            // Get the type
            Type type = value.GetType();

            // Get fieldinfo for this type
            FieldInfo fieldInfo = type.GetField(value.ToString());

            // Get the stringvalue attributes
            StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
                typeof(StringValueAttribute), false) as StringValueAttribute[];

            // Return the first if there was a match.
            return attribs.Length > 0 ? attribs[0].StringValue : null;
        }

        /// <summary>
        /// Retrieves the friendly name translated of specific language
        /// </summary>
        /// <returns>Returns A list of strings contains all the language friendly names translated in specific language</returns>
        public static List<string> GetLanguageNamesTranslation(this BingLanguage value)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (value == BingLanguage.None)
            {
                throw new Exception("Invalid Language");
            }

            List<string> Languages = new List<string>();
            try
            {
                BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
                Languages = client.GetLanguageNames(BingAppID, value.GetBingLangCode(), langsDic.Keys.ToArray()).ToList<string>();
                return Languages;
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        /// <summary>
        /// Retrieves a list of languages friendly names thats are supporting speaking in Bing Translator.
        /// </summary>
        /// <returns> Returns a list of strings contains the speaking supported languages friendly names</returns>
        public static List<string> GetSpeakingSupportedLanguages(this BingLanguage value)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (value == BingLanguage.None)
            {
                throw new Exception("Invalid Language");
            }

            List<string> Languages = new List<string>();
            try
            {
                BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
                string[] supportedlangs = client.GetLanguagesForSpeak(BingAppID);
                Languages = client.GetLanguageNames(BingAppID, value.GetBingLangCode(), supportedlangs).ToList<string>();
                return Languages;
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// Indicate if the selected language speaking is supported in Bing Translation APIs
        /// </summary>
        /// <returns>Returns a boolean value that indicates if the language is support speaking capability</returns>
        public static bool IsSpeakingSupported(this BingLanguage value)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (value == BingLanguage.None)
            {
                throw new Exception("Invalid Language");
            }

            try
            {
                BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
                string[] supportedlangs = client.GetLanguagesForSpeak(BingAppID);
                return supportedlangs.Contains(value.GetBingLangCode());
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        /// <summary>
        /// Retrieve a URL for the wave file that contain the speaking voice of the text
        /// </summary>
        /// <param name="destinationlang">Destination Language</param>
        /// <returns>Retruns a URI contains the wave file of the selected text speaking voide in the selected language</returns>
        public static Uri GetSpeakingWaveFile(this string text, BingLanguage destinationlang)
        {
            if (BingAppID == null || BingAppID.Trim().Length == 0)
            {
                throw new Exception("Invalid Bing App ID");
            }
            if (destinationlang == BingLanguage.None)
            {
                throw new Exception("Invalid Language");
            }
            if (text.Trim().Length == 0)
            {
                throw new Exception("There is no text to speak");
            }
            if (!text.DetectLanguage().IsSpeakingSupported())
            {
                throw new Exception("The selected language is not supported");
            }

            try
            {
                BingTranslationSvc.LanguageServiceClient client = new BingTranslationSvc.LanguageServiceClient();
                return new Uri(client.Speak(BingAppID,text,destinationlang.GetBingLangCode(),"audio/wav"));
            }
            catch (Exception)
            {
                
                throw;
            }
        }
        

    }

    public class StringValueAttribute : Attribute
    {

        #region Properties

        /// <summary>
        /// Holds the stringvalue for a value in an enum.
        /// </summary>
        public string StringValue { get; protected set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor used to init a StringValue Attribute
        /// </summary>
        /// <param name="value"></param>
        public StringValueAttribute(string value)
        {
            this.StringValue = value;
        }

        #endregion

    }
}
